package com.demandbreakdown.xiyun.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.demandbreakdown.xiyun.common.annotation.OperationLog;
import com.demandbreakdown.xiyun.common.base.ResultCode;
import com.demandbreakdown.xiyun.common.exception.BusinessException;
import com.demandbreakdown.xiyun.model.dto.auth.ForgotPasswordDTO;
import com.demandbreakdown.xiyun.model.dto.auth.SendCaptchaDTO;
import com.demandbreakdown.xiyun.model.dto.user.UserLoginDTO;
import com.demandbreakdown.xiyun.model.dto.user.UserRegisterDTO;
import com.demandbreakdown.xiyun.model.entity.User;
import com.demandbreakdown.xiyun.model.enums.UserRoleEnum;
import com.demandbreakdown.xiyun.model.enums.UserStatusEnum;
import com.demandbreakdown.xiyun.model.vo.auth.LoginVO;
import com.demandbreakdown.xiyun.model.vo.user.UserVO;
import com.demandbreakdown.xiyun.service.AuthService;
import com.demandbreakdown.xiyun.service.EmailService;
import com.demandbreakdown.xiyun.service.SecurityControlService;
import com.demandbreakdown.xiyun.service.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

import static com.demandbreakdown.xiyun.common.constant.SecurityConstant.TOKEN_BLACKLIST_EXPIRE_MINUTES;
import static com.demandbreakdown.xiyun.common.constant.SecurityConstant.TOKEN_BLACKLIST_PREFIX;

/**
 * 认证授权服务实现类
 *
 * @author poxz
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {

    // Service 层依赖
    private final EmailService emailService;
    private final UserService userService;
    private final SecurityControlService securityControlService;

    // 工具类依赖
    private final PasswordEncoder passwordEncoder;
    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    @OperationLog(value = "发送验证码", params = {"#sendCaptchaDTO.email"})
    public void sendCaptcha(SendCaptchaDTO sendCaptchaDTO) {
        String email = sendCaptchaDTO.getEmail();

        // 检查邮箱验证码发送频率限制
        if (securityControlService.checkEmailCaptchaLimit(email)) {
            throw new BusinessException(ResultCode.EMAIL_SEND_TOO_FREQUENT);
        }

        // 发送验证码
        emailService.sendCaptcha(email);

        // 记录验证码发送
        securityControlService.recordEmailCaptchaSent(email);
    }

    @Override
    @OperationLog(value = "用户注册", params = {"#userRegisterDTO.userAccount", "#userRegisterDTO.email"})
    @Transactional(rollbackFor = Exception.class)
    public LoginVO register(UserRegisterDTO userRegisterDTO) {
        String userAccount = userRegisterDTO.getUserAccount();
        String email = userRegisterDTO.getEmail();
        String userPassword = userRegisterDTO.getUserPassword();
        String checkPassword = userRegisterDTO.getCheckPassword();
        String captcha = userRegisterDTO.getCaptcha();

        // 验证邮箱验证码
        if (!emailService.verifyCaptcha(email, captcha)) {
            throw new BusinessException(ResultCode.EMAIL_CAPTCHA_ERROR);
        }

        // 验证两次密码是否一致
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ResultCode.USER_PASSWORD_NOT_MATCH);
        }

        // 检查账号是否已存在
        LambdaQueryWrapper<User> userQueryWrapper = new LambdaQueryWrapper<>();
        userQueryWrapper.eq(User::getUserAccount, userAccount);
        if (userService.count(userQueryWrapper) > 0) {
            throw new BusinessException(ResultCode.USER_ALREADY_EXISTS);
        }

        // 检查邮箱是否已注册
        userQueryWrapper = new LambdaQueryWrapper<>();
        userQueryWrapper.eq(User::getEmail, email);
        if (userService.count(userQueryWrapper) > 0) {
            throw new BusinessException(ResultCode.USER_ALREADY_EXISTS);
        }

        // 创建用户
        User user = User.builder()
                .userAccount(userAccount)
                .email(email)
                .userPassword(passwordEncoder.encode(userPassword))
                .userRole(UserRoleEnum.USER.getValue())
                .userStatus(UserStatusEnum.NORMAL.getValue())
                .build();

        boolean result = userService.save(user);
        if (!result) {
            throw new BusinessException(ResultCode.SYSTEM_ERROR);
        }

        // 自动登录并生成 Token
        StpUtil.login(user.getId());
        String token = StpUtil.getTokenValue();

        return LoginVO.builder()
                .token(token)
                .user(UserVO.from(user))
                .build();
    }

    @Override
    @OperationLog(value = "用户登录", params = {"#userLoginDTO.userAccount"})
    @Transactional(rollbackFor = Exception.class)
    public LoginVO login(UserLoginDTO userLoginDTO) {
        String userAccount = userLoginDTO.getUserAccount();
        String userPassword = userLoginDTO.getUserPassword();

        // 查询用户
        LambdaQueryWrapper<User> userQueryWrapper = new LambdaQueryWrapper<>();
        userQueryWrapper.eq(User::getUserAccount, userAccount);
        User user = userService.getOne(userQueryWrapper);

        if (user == null) {
            throw new BusinessException(ResultCode.USER_NOT_FOUND);
        }

        // 检查用户状态
        if (UserStatusEnum.DISABLED.getValue().equals(user.getUserStatus())) {
            throw new BusinessException(ResultCode.USER_ACCOUNT_DISABLED);
        }

        // 验证密码
        if (!passwordEncoder.matches(userPassword, user.getUserPassword())) {
            throw new BusinessException(ResultCode.USER_PASSWORD_ERROR);
        }

        // 更新最后登录时间
        user.setLastLoginTime(LocalDateTime.now());
        userService.updateById(user);

        // 登录并生成 Token
        StpUtil.login(user.getId());
        String token = StpUtil.getTokenValue();

        return LoginVO.builder()
                .token(token)
                .user(UserVO.from(user))
                .build();
    }


    @Override
    @OperationLog("退出登录")
    public void logout(String token) {
        // 将当前会话的 Token 加入黑名单
        if (StpUtil.isLogin()) {
            String tokenValue = StpUtil.getTokenValue();
            if (tokenValue != null && !tokenValue.isEmpty()) {
                redisTemplate.opsForValue().set(TOKEN_BLACKLIST_PREFIX + tokenValue, "1",
                        TOKEN_BLACKLIST_EXPIRE_MINUTES, TimeUnit.MINUTES);
            }
        }

        // 执行登出操作
        StpUtil.logout();
    }

    @Override
    @OperationLog(value = "忘记密码重置", params = {"#forgotPasswordDTO.email"})
    @Transactional(rollbackFor = Exception.class)
    public void forgotPassword(ForgotPasswordDTO forgotPasswordDTO) {
        String email = forgotPasswordDTO.getEmail();
        String captcha = forgotPasswordDTO.getCaptcha();
        String newPassword = forgotPasswordDTO.getNewPassword();
        String confirmPassword = forgotPasswordDTO.getConfirmPassword();

        // 验证两次密码是否一致
        if (!newPassword.equals(confirmPassword)) {
            throw new BusinessException(ResultCode.USER_PASSWORD_NOT_MATCH);
        }

        // 验证邮箱验证码
        if (!emailService.verifyCaptcha(email, captcha)) {
            throw new BusinessException(ResultCode.EMAIL_CAPTCHA_ERROR);
        }

        // 检查邮箱对应的用户是否存在
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getEmail, email);
        User user = userService.getOne(queryWrapper);

        if (user == null) {
            throw new BusinessException(ResultCode.USER_NOT_FOUND);
        }

        // 检查用户状态
        if (!UserStatusEnum.NORMAL.getValue().equals(user.getUserStatus())) {
            throw new BusinessException(ResultCode.USER_ACCOUNT_DISABLED);
        }

        // 加密并更新密码
        String encryptedPassword = passwordEncoder.encode(newPassword);
        user.setUserPassword(encryptedPassword);
        boolean updateResult = userService.updateById(user);

        if (!updateResult) {
            throw new BusinessException(ResultCode.SYSTEM_ERROR);
        }

        // 踢出该用户的所有登录会话（强制重新登录）
        StpUtil.kickout(user.getId());
    }
}
